using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using KiloWatt.Base.Graphics;
using KiloWatt.Base.Animation;

namespace Game1
{
    public  class Animated : DrawableGameComponent
    {

        ModelDraw loadedModel;             //  loaded geometry
        BoundingSphere modelSize;          //  calculated size of the model
        AnimationSet animations;           //  the animations I have to choose from
        AnimationInstance[] instances;     //  the animation data, as loaded
        IBlendedAnimation[] blended;       //  state about the different animations (that can change)
        AnimationBlender blender;          //  object that blends between playing animations
        int curAnimationInstance;     //  which animation is playing? (-1 for none)
        Camera camera;
        DrawDetails drawDet;
      //  DebugLines debugLines;

        public Animated(String modelname,Camera camera) : base(Base.Singleton)
        {//   debugLines = new DebugLines(Base.getGraphicsDevice().GraphicsDevice);
            
            this.camera = camera;
            //  clear current state
            curAnimationInstance = -1;
            instances = null;
            blended = null;
            loadedModel = new ModelDraw(Base.Content.Load<Model>("Models\\" + modelname), modelname);
            blender = new AnimationBlender(loadedModel.Model, loadedModel.Name);
            drawDet = new DrawDetails();
            curAnimationInstance = -1;
            instances = null;
            blended = null;
            //  get the list of animations from our dictionary
            Dictionary<string, object> tag = loadedModel.Model.Tag as Dictionary<string, object>;
            object aobj = null;
            if (tag != null)
                tag.TryGetValue("AnimationSet", out aobj);
            animations = aobj as AnimationSet;

            //  set up animations
            if (animations != null)
            {
                instances = new AnimationInstance[animations.NumAnimations];
                //  I'll need a BlendedAnimation per animation, so that I can let the 
                //  blender object transition between them.
                blended = new IBlendedAnimation[instances.Length];
                int ix = 0;
                foreach (Animation a in animations.Animations)
                {
                    instances[ix] = new AnimationInstance(a);
                    blended[ix] = AnimationBlender.CreateBlendedAnimation(instances[ix]);
                    ++ix;
                }
            }
        }

        public override void Update(GameTime gameTime) 
        {
            float dt = (float)gameTime.ElapsedGameTime.TotalSeconds;
            if (dt > 0.1f) dt = 0.1f;
            if (blender != null)
                blender.Advance(dt);
        }

        public override void Draw(GameTime gameTime)
        {
            if (loadedModel != null)
            {
                //  the drawdetails set-up can be re-used for all items in the scene
                DrawDetails dd = drawDet;
                dd.dev = Base.getGraphicsDevice().GraphicsDevice;
                dd.fogColor = new Vector4(0.5f, 0.5f, 0.5f, 1);
                dd.fogDistance = 10000;
                dd.lightAmbient = new Vector4(0.2f, 0.2f, 0.2f, 1.0f);
                dd.lightDiffuse = new Vector4(0.8f, 0.8f, 0.8f, 0);
                dd.lightDir = Vector3.Normalize(new Vector3(1, 3, 2));

                dd.viewInv = Matrix.Invert(camera.View);
                dd.viewProj = camera.View*camera.Projection ;
                dd.world = Matrix.CreateTranslation(new Vector3(5.0f,5.0f,5.0f));

                //  draw the loaded model
                loadedModel.Draw(dd, blender);
            }
            //  when everything else is drawn, Z sort and draw the transparent parts
            ModelDraw.DrawDeferred();

            //debugLines.Draw(camera.View, camera.Projection);
           // debugLines.Reset();
        }

        public void setCurAnimation(int i, float dt)
        {
            if (instances != null && curAnimationInstance < instances.Length - 1)
                curAnimationInstance = i;
            if (curAnimationInstance > 0)
                instances[curAnimationInstance].Time = instances[curAnimationInstance - 1].Time;
            blender.TransitionAnimations(GetBlended(curAnimationInstance - 1), GetBlended(curAnimationInstance), 1.0f);
            
        }

        IBlendedAnimation GetBlended(int ix)
        {
            unchecked
            {
                return (ix < 0) ? null : (ix >= blended.Length) ? null : blended[ix];
            }
        }

        public void previousAnimation()
        {
            if (curAnimationInstance > 0)
            {
                curAnimationInstance -= 1;
                if (curAnimationInstance > 0)
                    instances[curAnimationInstance].Time = instances[curAnimationInstance - 1].Time;
                blender.TransitionAnimations(GetBlended(curAnimationInstance + 1), GetBlended(curAnimationInstance), 1.0f);
            }

        }

        public void nextAnimation()
        {
            if (curAnimationInstance < instances.Length - 1)
            {
                curAnimationInstance += 1;
                if (curAnimationInstance > 0)
                    instances[curAnimationInstance].Time = instances[curAnimationInstance - 1].Time;
                blender.TransitionAnimations(GetBlended(curAnimationInstance - 1), GetBlended(curAnimationInstance), 1.0f);
            }
        }
    }
}
